package fei.typhoon.engine.android.sprite;

import android.graphics.Canvas;
import fei.typhoon.engine.android.FeiScreen;
import fei.typhoon.engine.load.FeiBitmap;
import fei.typhoon.engine.math.FeiMath;

// 帧动画的功能设计相对完善，图像变换中，未对暂停考虑，也应像帧动画一样设置暂停功能，另外需要添加透明度的渐变动画

public class FeiAnimation extends FeiImage {
	/** 停止状态 */
	private static final byte STATE_STOP = 0;
	/** 播放状态 */
	private static final byte STATE_RUNNING = 1;
	/** 暂停状态 */
	private static final byte STATE_PAUSE = 2;
	/** 销毁状态 */
	private static final byte STATE_DISPOSE = 3;

	/** 没有action */
	private static final byte ACTION_NONE = 0x00;
	/** 旋转action */
	private static final byte ACTION_ROTATE = 0x01;
	/** 缩放Xaction */
	private static final byte ACTION_SCALE_X = 0x02;
	/** 缩放Yaction */
	private static final byte ACTION_SCALE_Y = 0x04;
	/** 移动action */
	private static final byte ACTION_MOVE = 0x08;
	/** 透明度渐变action */
	private static final byte ACTION_ALPHA = 0x10;

	/** 默认帧播放换帧间隔时间 */
	protected static final float DEFAULT_SECONDS_PER_FRAME = 0.03f;

	// TODO 目前尚有部分功能未实现，待需要时实现。
	// ================================帧动画相关参数================================
	/** 帧管理器 */
	private FeiBitmap[] frames = null;
	/** 本动画已经播放的时长 */
	private float runningTime = 0.0f;
	/** 播放帧动画的每帧间隔时长 */
	private float frameTime = DEFAULT_SECONDS_PER_FRAME;
	/** 当前状态 */
	private byte state = STATE_STOP;
	/** 停止播放时所停留的帧 */
	private int stop = 0;
	/** 是否循环播放 */
	private boolean looping = true;
	/** 循环播放次数，默认值0表示无次数限制 */
	private int loopCount = 0;
	/** 暂停是已经帧动画已经播放的时间 */
	private float pauseRunningTime = 0;
	/** 启动或唤醒帧动画时的系统时间记录 */
	private long startNewRunningTime = 0;
	/** 本动画播放的总时长，默认值0表示无时间限制 */
	private float limitTime = 0.0f;
	/** 暂停渐变时候的系统时间 */
	private long pauseActionTime = 0;

	// ================================图像变换动画相关参数================================
	/** 透明度渐变需要的时间 */
	private float alphaTime = 0;
	/** 透明度渐变开始的系统时间记录 */
	private long alphaStart = 0;
	/** 透明度渐变 by是差，from是渐变前的alpha值，to是渐变目的alpha值 */
	private float alphaBy = 0.0f, alphaFrom = 255.0f, alphaTo = 255.0f;

	/** 当前被设置的变换动画集合 */
	private byte action = ACTION_NONE;
	/** 设置的移动到目的地的时间 */
	private float moveTime = 0;
	/** 开始移动的系统时间记录 */
	private long moveStart = 0;
	/** X方向 by是移动的位移差，from是移动前的位置，to是移动目的位置 */
	private float moveByX = 0, moveFromX = 0, moveToX = 0;
	/** Y方向 by是移动的位移差，from是移动前的位置，to是移动目的位置 */
	private float moveByY = 0, moveFromY = 0, moveToY = 0;

	/** 设置的旋转目的角度的时间 */
	private float rotateTime = 0;
	/** 开始旋转的系统时间记录 */
	private long rotateStart = 0;
	/** by是旋转的角度差，from是旋转前的角度，to是旋转目的角度 */
	private float rotateByDegrees = 0, rotateFromDegrees = 0,
			rotateToDegrees = 0;

	/** X方向 设置的缩放目的倍数的时间 */
	private float scaleXTime = 0;
	/** X方向 开始缩放的系统时间记录 */
	private long scaleXStart = 0;
	/** X方向 by是缩放的倍数差，from是缩放前的倍数，to是缩放目的倍数 */
	private float scaleXBy = 0.0f, scaleXFrom = 1.0f, scaleXTo = 1.0f;
	/** Y方向 设置的缩放目的倍数的时间 */
	private float scaleYTime = 0;
	/** Y方向 开始缩放的系统时间记录 */
	private long scaleYStart = 0;
	/** Y方向 by是缩放的倍数差，from是缩放前的倍数，to是缩放目的倍数 */
	private float scaleYBy = 0.0f, scaleYFrom = 1.0f, scaleYTo = 1.0f;

	public FeiAnimation(FeiScreen screen) {
		super(screen, null);
	}

	public FeiAnimation(FeiScreen screen, FeiBitmap bitmap) {
		super(screen, bitmap);
	}

	/**
	 * @param frames
	 *            动画帧
	 */
	public FeiAnimation(FeiScreen screen, FeiBitmap... frames) {
		super(screen, (frames != null && frames.length > 0) ? frames[0]
				: (FeiBitmap) null);
		setFrames(frames);
	}

	/**
	 * @return 计算当前帧停留的时间，根据精灵状态，获取即将绘制的帧
	 */
	public synchronized FeiBitmap getFrame() {
		if (state == STATE_DISPOSE || frames == null || frames.length == 0) {
			return bitmap;
		}
		if (state == STATE_RUNNING) {
			runningTime = pauseRunningTime
					+ ((float) (System.currentTimeMillis() - startNewRunningTime) / 1000.0f);
		} else if (state == STATE_STOP) {
			if (stop < frames.length) {
				bitmap = frames[stop];
				return bitmap;
			}
		}

		int frameNumber = (int) (runningTime / frameTime);

		if (!looping) {
			frameNumber = Math.min(frames.length - 1, frameNumber);
		} else {
			frameNumber = frameNumber % frames.length;
		}
		bitmap = frames[frameNumber];
		return bitmap;
	}

	/**
	 * 暂停
	 */
	public synchronized void pauseFrames() {
		pauseRunningTime = runningTime;
		startNewRunningTime = 0;
		runningTime = 0;
	}

	/**
	 * 恢复暂停，继续播放动画
	 */
	public synchronized void resumeFrames() {
		if (state == STATE_PAUSE) {
			startNewRunningTime = System.currentTimeMillis();
		}
	}

	/**
	 * 开启动画播放功能
	 */
	public synchronized void startFrames() {
		pauseRunningTime = 0;
		runningTime = 0;
		startNewRunningTime = System.currentTimeMillis();
		state = STATE_RUNNING;
	}

	/**
	 * 停止动画播放功能
	 */
	public synchronized void stopFrames() {
		state = STATE_STOP;
		pauseRunningTime = 0;
		startNewRunningTime = 0;
		runningTime = 0;
	}

	/**
	 * 销毁
	 */
	public synchronized void destroyFrames() {
		state = STATE_DISPOSE;
		if (frames != null) {
			frames = null;
		}
	}

	public synchronized void alphaTo(float alpha, float time) {
		if (paint != null) {
			alphaBy(alpha - paint.getAlpha(), time);
		}
	}

	public synchronized void alphaBy(float deltaAlpha, float time) {
		if (paint != null) {
			alphaBy = deltaAlpha;
			alphaFrom = paint.getAlpha();
			alphaTo = alphaFrom + deltaAlpha;
			alphaTime = time;
			alphaStart = System.currentTimeMillis();
			action |= ACTION_ALPHA;
		}
	}

	/**
	 * 暂停
	 */
	public synchronized void pauseAction() {
		pauseActionTime = System.currentTimeMillis();
	}

	/**
	 * 恢复暂停，继续播放动画
	 */
	public synchronized void resumeAction() {
		if (state == STATE_PAUSE) {
			if (!alphaEndCheck()) {
				long alphaRunningTime = pauseActionTime - alphaStart;
				alphaStart = System.currentTimeMillis() - alphaRunningTime;
			}
			if (!moveEndCheck()) {
				long moveRunningTime = pauseActionTime - moveStart;
				moveStart = System.currentTimeMillis() - moveRunningTime;
			}
			if (!scaleXEndCheck()) {
				long scaleXRunningTime = pauseActionTime - scaleXStart;
				scaleXStart = System.currentTimeMillis() - scaleXRunningTime;
			}
			if (!scaleYEndCheck()) {
				long scaleYRunningTime = pauseActionTime - scaleYStart;
				scaleYStart = System.currentTimeMillis() - scaleYRunningTime;
			}
			if (!rotateEndCheck()) {
				long rotateRunningTime = pauseActionTime - rotateStart;
				rotateStart = System.currentTimeMillis() - rotateRunningTime;
			}
		}
	}

	public synchronized void moveTo(float x, float y, float time) {
		moveBy(x - this.x, y - this.y, time);
	}

	public synchronized void moveBy(float x, float y, float time) {
		moveByX = x;
		moveByY = y;
		moveFromX = this.x;
		moveFromY = this.y;
		moveToX = this.x + x;
		moveToY = this.y + y;
		moveTime = time;
		moveStart = System.currentTimeMillis();
		action |= ACTION_MOVE;
	}

	public synchronized void rotateBy(float degrees, float time) {
		rotateByDegrees = degrees;
		rotateFromDegrees = rotate;
		rotateToDegrees = rotate + degrees/*                           * MathUtils.degreesToRadians */;
		rotateTime = time;
		rotateStart = System.currentTimeMillis();
		action |= ACTION_ROTATE;
	}

	public synchronized void scaleXBy(float multipleX, float time) {
		scaleXFrom = scaleX;
		scaleXTo = scaleX * multipleX;
		scaleXBy = scaleXTo - scaleXFrom;
		scaleXTime = time;

		scaleXStart = System.currentTimeMillis();
		action |= ACTION_SCALE_X;
	}

	public synchronized void scaleYBy(float multipleY, float time) {
		scaleYFrom = scaleY;
		scaleYTo = scaleY * multipleY;
		scaleYBy = scaleYTo - scaleYFrom;
		scaleYTime = time;

		scaleYStart = System.currentTimeMillis();
		action |= ACTION_SCALE_Y;
	}

	public synchronized void scaleBy(float multiple, float time) {
		scaleBy(multiple, multiple, time);
	}

	public synchronized void scaleBy(float multipleX, float multipleY,
			float time) {
		scaleXBy(multipleX, time);
		scaleYBy(multipleY, time);
	}

	public boolean alphaEndCheck() {
		if ((action & ACTION_ALPHA) == 0) {
			return true;
		}
		if (paint.getAlpha() == alphaTo) {
			action ^= ACTION_ALPHA;
			alphaStart = 0;
			alphaTime = 0;
			alphaTo = 0xFF;
			alphaFrom = 0xFF;
			alphaBy = 0;
			return true;
		} else {
			return false;
		}
	}

	public boolean moveEndCheck() {
		if ((action & ACTION_MOVE) == 0) {
			return true;
		}
		if (x == moveToX && y == moveToY) {
			action ^= ACTION_MOVE;
			moveStart = 0;
			moveTime = 0;
			moveToX = 0;
			moveToY = 0;
			moveFromX = 0;
			moveFromY = 0;
			moveByX = 0;
			moveByY = 0;
			return true;
		} else {
			return false;
		}
	}

	public boolean rotateEndCheck() {
		if ((action & ACTION_ROTATE) == 0) {
			return true;
		}
		if (rotate == rotateToDegrees) {
			action ^= ACTION_ROTATE;
			rotateByDegrees = 0;
			rotateFromDegrees = 0;
			rotateToDegrees = 0;
			rotateStart = 0;
			rotateTime = 0;
			return true;
		} else {
			return false;
		}
	}

	public boolean scaleXEndCheck() {
		if ((action & ACTION_SCALE_X) == 0) {
			return true;
		}
		if (scaleX == scaleXTo) {
			action ^= ACTION_SCALE_X;
			scaleXBy = 0f;
			scaleXFrom = 1.0f;
			scaleXTo = 1.0f;
			scaleXStart = 0;
			scaleXTime = 0;
			return true;
		} else {
			return false;
		}
	}

	public boolean scaleYEndCheck() {
		if ((action & ACTION_SCALE_Y) == 0) {
			return true;
		}
		if (scaleY == scaleYTo) {
			action ^= ACTION_SCALE_Y;
			scaleYBy = 0f;
			scaleYFrom = 1.0f;
			scaleYTo = 1.0f;
			scaleYStart = 0;
			scaleYTime = 0;
			return true;
		} else {
			return false;
		}
	}

	public float getFrameTime() {
		return frameTime;
	}

	public void setFrameTime(float frameTime) {
		this.frameTime = frameTime;
	}

	public int getStop() {
		return stop;
	}

	public void setStop(int stop) {
		this.stop = stop;
	}

	public float getRunningTime() {
		return runningTime;
	}

	public void setRunningTime(float runningTime) {
		this.runningTime = runningTime;
	}

	public boolean isLooping() {
		return looping;
	}

	public void setLooping(boolean looping) {
		this.looping = looping;
	}

	public int getLoopCount() {
		return loopCount;
	}

	public void setLoopCount(int loopCount) {
		this.loopCount = loopCount;
	}

	public void setFrames(FeiBitmap[] frames) {
		this.frames = frames;
	}

	public void setFrames(String[] res) {

	}

	public void setFrames(int[] id) {

	}

	public FeiBitmap[] getFrames() {
		return frames;
	}

	public float getAlpha() {
		float alpha = paint.getAlpha();
		if (!alphaEndCheck()) {
			long runningTime = (state == STATE_PAUSE) ? pauseActionTime
					: System.currentTimeMillis();
			alpha = alphaFrom + (float) (runningTime - alphaStart) / 1000.0f
					* alphaBy / alphaTime;
			if ((alphaBy >= 0 && alpha >= alphaTo)
					|| (alphaBy < 0 && alpha <= alphaTo)) {
				alpha = alphaTo;
				alphaEndCheck();
			}
		}
		return alpha;
	}

	public float getX() {
		if (!moveEndCheck() && x != moveToX) {
			long runningTime = (state == STATE_PAUSE) ? pauseActionTime
					: System.currentTimeMillis();
			x = moveFromX + (float) (runningTime - moveStart) / 1000.0f
					* moveByX / moveTime;
			if ((moveByX >= 0 && x >= moveToX) || (moveByX < 0 && x <= moveToX)) {
				x = moveToX;
				moveEndCheck();
			}
		}
		return x;
	}

	public float getY() {
		if (!moveEndCheck() && y != moveToY) {
			long runningTime = (state == STATE_PAUSE) ? pauseActionTime
					: System.currentTimeMillis();
			y = moveFromY + (float) (runningTime - moveStart) / 1000.0f
					* moveByY / moveTime;
			if ((moveByY >= 0 && y >= moveToY) || (moveByY < 0 && y <= moveToY)) {
				y = moveToY;
				moveEndCheck();
			}
		}
		return y;
	}

	public float getRotate() {
		if (!rotateEndCheck()) {
			long runningTime = (state == STATE_PAUSE) ? pauseActionTime
					: System.currentTimeMillis();
			rotate = rotateFromDegrees + (float) (runningTime - rotateStart)
					/ 1000.0f * rotateByDegrees / rotateTime;
			if ((rotateByDegrees >= 0 && rotate >= rotateToDegrees)
					|| (rotateByDegrees < 0 && rotate <= rotateToDegrees)) {
				rotate = rotateToDegrees;
				rotateEndCheck();
			}
		}
		return rotate;
	}

	public float getScaleX() {
		if (!scaleXEndCheck()) {
			long runningTime = (state == STATE_PAUSE) ? pauseActionTime
					: System.currentTimeMillis();
			scaleX = scaleXFrom + (float) (runningTime - scaleXStart) / 1000.0f
					* scaleXBy / scaleXTime;
			if ((scaleXBy >= 0 && scaleX >= scaleXTo)
					|| (scaleXBy < 0 && scaleX <= scaleXTo)) {
				scaleX = scaleXTo;
				scaleXEndCheck();
			}
		}
		return scaleX;
	}

	public float getScaleY() {
		if (!scaleYEndCheck()) {
			long runningTime = (state == STATE_PAUSE) ? pauseActionTime
					: System.currentTimeMillis();
			scaleY = scaleYFrom + (float) (runningTime - scaleYStart) / 1000.0f
					* scaleYBy / scaleYTime;
			if ((scaleYBy >= 0 && scaleY >= scaleYTo)
					|| (scaleYBy < 0 && scaleY <= scaleYTo)) {
				scaleY = scaleYTo;
				scaleYEndCheck();
			}
		}
		return scaleY;
	}

	public float getLimitTime() {
		return limitTime;
	}

	public void setLimitTime(float limitTime) {
		this.limitTime = limitTime;
	}

	public long getMoveStart() {
		return moveStart;
	}

	public void setMoveStart(long moveStart) {
		this.moveStart = moveStart;
	}

	public float getMoveTime() {
		return moveTime;
	}

	public void setMoveTime(float moveTime) {
		this.moveTime = moveTime;
	}

	public float getMoveToX() {
		return moveToX;
	}

	public void setMoveToX(float moveToX) {
		this.moveToX = moveToX;
	}

	public float getMoveToY() {
		return moveToY;
	}

	public void setMoveToY(float moveToY) {
		this.moveToY = moveToY;
	}

	public long getRotateStart() {
		return rotateStart;
	}

	public void setRotateStart(long rotateStart) {
		this.rotateStart = rotateStart;
	}

	public float getRotateTime() {
		return rotateTime;
	}

	public void setRotateTime(float rotateTime) {
		this.rotateTime = rotateTime;
	}

	public void draw(Canvas canvas) {
		setBitmap(getFrame());
		setAlpha(FeiMath.floatToInt(getAlpha()));
		super.draw(canvas);
	}

	@Override
	public synchronized void destroy() {
		super.destroy();
	}

	@Override
	public synchronized void pause() {
		state = STATE_PAUSE;
		pauseFrames();
		pauseAction();
	}

	@Override
	public synchronized void resume() {
		resumeFrames();
		resumeAction();
		state = STATE_RUNNING;
	}

}
